﻿#region 참조 네임스페이스

using System;
using System.Net;
using System.Windows.Forms;
using Skying.ComponentModel;
using Skying.Net.NetworkInformation;
using Skying.Net.Sockets;
using System.ComponentModel;
using Skying.Threading;
using System.Text;

#endregion

namespace TcpClient
{
	public partial class MainForm : Form
	{
		#region 변수

		/// <summary>
		/// 서버와의 통신을 담당하는 클라이언트를 나타낸다.
		/// </summary>
		private Skying.Net.Sockets.TcpClient _client;

		/// <summary>
		/// 로깅을 위한 데이터 그리드 바인딩 리스트를 나타낸다.
		/// </summary>
		private SortableBindingList<Log> _logBindingList;

		/// <summary>
		/// 메시지를 반복적으로 전송하기 위한 동기화 타이머를 나타낸다.
		/// </summary>
		private SynchronizingTimer _sendRepeatedlyTimer;

		#endregion

		#region 생성자/소멸자

		public MainForm()
		{
			this.InitializeComponent();

			this._logBindingList = new SortableBindingList<Log>();

			this._sendRepeatedlyTimer = new SynchronizingTimer();

			this.LogDataGridView.DataSource = this._logBindingList;

			this.Load += (s, e) =>
			{
				IPAddress ipAddress;

				if (NetworkInformation.GetActiveIpAddress(out ipAddress))
				{
					this.IpAddressTextBox.Text = ipAddress.ToString();
				}
			};
		}

		#endregion

		#region 로깅

		/// <summary>
		/// 로그 그리드 뷰에 로그를 남긴다.
		/// </summary>
		/// <param name="logString">로그 문자열을 설정한다.</param>
		private void Log(string logString)
		{
			try
			{
				string nowString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff");

				this._logBindingList.Add(new Log(nowString, logString));

				this.LogDataGridView.Sort(this.LogDataGridView.Columns[0], ListSortDirection.Descending);
			}
			catch (Exception error)
			{
				
			}
		}

		#endregion

		#region 반복 메시지 전송

		/// <summary>
		/// 반복 메시지 타이머를 시작한다.
		/// </summary>
		private void StartSendRepeatedlyTimer()
		{
			try
			{
				this._sendRepeatedlyTimer.Elapsed += () =>
				{
					this._client.SendMessage(this.MessageTextBox.Text);
				};

				this._sendRepeatedlyTimer.SetPeriodic(new TimeSpan(0, 0, 0, 0, (int)this.RepeatSpanNumericUpDown.Value));
			}
			catch (Exception error)
			{

			}
		}

		/// <summary>
		/// 반복 메시지 타이머를 정지한다.
		/// </summary>
		private void StopSendRepeatedlyTimer()
		{
			try
			{
				this._sendRepeatedlyTimer.Cancel();
			}
			catch (Exception error)
			{

			}
		}

		#endregion

		#region 버튼 이벤트

		/// <summary>
		/// Connect 버튼이 클릭된 경우 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ConnectButton_Click(object sender, EventArgs e)
		{
			try
			{
				IPAddress ipAddress;
				int portNumber;

				if (!IPAddress.TryParse(this.IpAddressTextBox.Text, out ipAddress))
				{
					return;
				}

				portNumber = (int)this.PortNumericUpDown.Value;

				this._client = new Skying.Net.Sockets.TcpClient(this.UseInternalHeaderCheckBox.Checked);

				this._client.Connected = (eventArgs) =>
				{
					string logString = string.Empty;

					if (eventArgs.Error == null)
					{
						logString = string.Format("Connected to {0}", this._client.Address, this._client.Port);

						this.ConnectionStripStatusLabel.Text = logString;

						this.ConnectButton.Enabled = false;
						this.DisconnectButton.Enabled = true;
						this.SendButton.Enabled = true;
						this.SendFileButton.Enabled = true;
						this.SendObjectButton.Enabled = true;
						this.SendRepeatedlyButton.Enabled = true;
						this.UseInternalHeaderCheckBox.Enabled = false;
					}
					else
					{
						logString = eventArgs.Error.Message;
					}

					this.Log(logString);
				};

				this._client.Disconnected = (eventArgs) =>
				{
					this.ConnectionStripStatusLabel.Text = "Disconnected";

					this.ConnectButton.Enabled = true;
					this.DisconnectButton.Enabled = false;
					this.SendButton.Enabled = false;
					this.SendFileButton.Enabled = false;
					this.SendObjectButton.Enabled = false;
					this.SendRepeatedlyButton.Enabled = false;
					this.UseInternalHeaderCheckBox.Enabled = true;

					this.Log("Disconnected");
				};

				this._client.Received = (eventArgs) =>
				{
					TransferEventArgs result = eventArgs.Result;

					string logString = string.Empty;

					Header header = result.Header;

					switch (result.Header.PacketType)
					{
						case PacketType.Raw:

							byte[] buffer = new byte[result.DataStream.Length];

							result.DataStream.Read(buffer, 0, buffer.Length);

							logString = string.Format("[R] - Raw (Value: {0})", Encoding.UTF8.GetString(buffer));

							break;

						case PacketType.Serialized:

							if (header.DataType.Equals(typeof(DateTime).FullName))
							{
								DateTime dataObject = (DateTime)result.DataObject;

								logString = string.Format("[R] - Serialized Object (Type: {0}, Value: {1})", header.DataType, dataObject);
							}

							break;

						case PacketType.Message:

							logString = string.Format("[R] - Message (Value: {0})", result.DataMessage);

							break;

						case PacketType.File:

							break;
					}

					this.Log(logString);
				};

				this._client.Sent = (eventArgs) =>
				{
					TransferEventArgs result = eventArgs.Result;

					string logString = string.Empty;

					Header header = result.Header;

					switch (result.Header.PacketType)
					{
						case PacketType.Serialized:

							if (header.DataType.Equals(typeof(DateTime).FullName))
							{
								logString = string.Format("[S] - Serialized Object (Type: {0})", header.DataType);
							}

							break;

						case PacketType.Message:

							logString = "[S] - Message";

							break;

						case PacketType.File:

							break;
					}

					this.Log(logString);
				};

				this._client.Error = (eventArgs) =>
				{
					this.Log(eventArgs.Error.Message);
				};

				this._client.ConnectAsynchrous(new IPEndPoint(ipAddress, portNumber), "UserToken Test");
			}
			catch (Exception error)
			{

			}
		}

		/// <summary>
		/// Disconnect 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void DisconnectButton_Click(object sender, EventArgs e)
		{
			try
			{
				this._client.DisconnectAsynchrous();
			}
			catch (Exception error)
			{

			}
		}

		/// <summary>
		/// Send 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void SendButton_Click(object sender, EventArgs e)
		{
			try
			{
				this._client.SendMessage(this.MessageTextBox.Text);
			}
			catch (Exception error)
			{

			}
		}

		/// <summary>
		/// Send File 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void SendFileButton_Click(object sender, EventArgs e)
		{
			try
			{
				OpenFileDialog dialog = new OpenFileDialog() { };

				if (dialog.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
				{
					this._client.SendFile(dialog.FileName);
				}
			}
			catch (Exception error)
			{

			}
		}

		/// <summary>
		/// Send Object 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void SendObjectButton_Click(object sender, EventArgs e)
		{
			try
			{
				this._client.SendObject(DateTime.Now);
			}
			catch (Exception error)
			{

			}
		}

		/// <summary>
		/// Send Repeatedly 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void SendRepeatedlyButton_Click(object sender, EventArgs e)
		{
			try
			{
				switch (this._sendRepeatedlyTimer.Enabled)
				{
					case true:

						this.StopSendRepeatedlyTimer();

						break;

					case false:

						this.StartSendRepeatedlyTimer();

						break;
				}
			}
			catch (Exception error)
			{
				
			}
		}

		#endregion

		#region 패널 이벤트

		/// <summary>
		/// 메시지 텍스트 박스의 패널이 리사이즈 됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void panel4_Resize(object sender, EventArgs e)
		{
			Panel panel = sender as Panel;

			if (panel.Controls.Count > 0)
			{
				panel.Controls[0].Width = panel.Width - panel.Controls[0].Left - panel.Padding.Left - panel.Padding.Right;
			}
		}

		#endregion
	}
}